/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-03-28     12804       the first version
 */
#include <rtthread.h>
#include "flash.h"
#include <stdlib.h>
uint16_t os_spiflash_read(void *buf, uint32_t addr, uint16_t len);
void os_spiflash_program(void *buf, uint32_t addr, uint16_t len);
void os_spiflash_erase(uint32_t addr);

static uint8_t buff[SECTOR_SIZE];

uint8_t FlashCheckSectorErase(uint32_t addr, uint8_t *data)
{
    uint16_t i;
    FlashRead(addr, data, SECTOR_SIZE);
    for(i=0;i<SECTOR_SIZE;i++)
    {
        if(data[i] != 0xFF)
            return 1;
    }
    return 0;
}


uint32_t  FlashGetSectorAddr(uint32_t addr)
{
    return (addr - (addr % SECTOR_SIZE));
}

uint8_t FlashWritePage(uint32_t pageAddress, uint8_t *data)
{
    uint16_t i;
    for(i=0;i<SECTOR_SIZE/256;i++)
    {
        os_spiflash_program(&data[256*i], pageAddress+256*i, 256);
    }
    return 1;
}


void FlashWrite(uint32_t addr, const uint8_t *data, size_t len)
{
    int i,j;
        if((addr >= DATA_ADDRESS_START) && ((addr + len) < DATA_ADDRESS_END))
        {
            uint32_t sectorAssress;
            uint16_t writed = 0;
            uint16_t toWrite;
            uint16_t offset;

            while(writed < len)
            {
                sectorAssress = FlashGetSectorAddr(addr);
                if(FlashCheckSectorErase(sectorAssress, buff) == 1)
                {
                    os_spiflash_erase(sectorAssress);
                }
                offset = addr - sectorAssress;
                toWrite = ((len - writed) < (SECTOR_SIZE - offset) ? (len - writed) : (SECTOR_SIZE - offset));
                rt_memmove(buff + offset, data + writed, toWrite);
                if(FlashWritePage(sectorAssress, buff)==0)
                    break;

                writed += toWrite;
                addr += toWrite;
            }
        }
}

void FlashErase(uint32_t addr, size_t len)
{
        int i,j;
        if((addr >= DATA_ADDRESS_START) && ((addr + len) < DATA_ADDRESS_END))
        {
            uint32_t sectorAssress;
            uint16_t writed = 0;
            uint16_t toWrite;
            uint16_t offset;

            while(writed < len)
            {
                sectorAssress = FlashGetSectorAddr(addr);
                if(FlashCheckSectorErase(sectorAssress, buff) == 1)
                {
                    os_spiflash_erase(sectorAssress);
                }
                offset = addr - sectorAssress;
                toWrite = ((len - writed) < (SECTOR_SIZE - offset) ? (len - writed) : (SECTOR_SIZE - offset));
                rt_memset(buff + offset, 0xFF, toWrite);
                if(FlashWritePage(sectorAssress, buff)==0)
                    break;

                writed += toWrite;
                addr += toWrite;
            }
        }
}

void FlashRead(uint32_t addr, uint8_t *data, size_t len)
{
    os_spiflash_read(data, addr, len);
}

uint8_t temp_buf[2048];
void flash(int argc, char **argv)
{
    int i,j,offset=0;
    if(argc > 1)
    {
        if(argc > 2)
        {
            offset = atoi(argv[2]);
        }
        switch(argv[1][0])
        {
        case 'w':;
            for(i=0;i<2048;i++)
            {
                temp_buf[i] = i%256;
            }
            FlashWrite(DATA_ADDRESS_START+offset,temp_buf, 2048);
            rt_kprintf("write ok\n");
            break;
        case 'r':
            FlashRead(DATA_ADDRESS_START+offset,temp_buf, sizeof(temp_buf));
            for(i=0;i<sizeof(temp_buf)/64;i++)
            {
                rt_kprintf("\n");
                rt_kprintf("%04d ", i*64);
                for(j=0;j<64;j++)
                {
                    rt_kprintf("%02X", temp_buf[i*64+j]);
                }
            }
            rt_kprintf("\n");
            break;
        case 'e':
            os_spiflash_erase(DATA_ADDRESS_START);
            rt_kprintf("erase ok\n");
            break;
        default:
            break;
        }
    }
}
MSH_CMD_EXPORT(flash, flash);
